package com.stropheware.droidDefense;

import com.stropheware.pathfinding.Node;
import com.stropheware.pathfinding.PathFinder;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.RectShape;
import android.util.Log;

public class GameBoard {
	
	public static final int INIT_NUM_CELLS_ACROSS = 35;
	public static final int MINIMUM_MENU_WIDTH = 70;
	public static final int MINIMUM_BOTTOM_MENU_HEIGHT = 25;
	public static final int MINIMUM_MONSTER_PATH = 3; //needs to be even! TODO might want to remove this
	public static final int PADDING = 5;

	private static final String TAG = "GameBoard";
	
	public Cell [][] cellGrid;
	
	public Node[][] nodes;
	
	/*
	 * Total number of cells 
	 * Always equal to numCellsWide * numCellsHeight
	 */
	public int numCells;
	
	/*
	 * represents in pixels, length of one side of cell
	 */
	public int cellSideLength;
	
	/*
	 * Number of cells across screen
	 */
	public int numCellsWide;
	
	/*
	 * Number of cells height of screen
	 */
	public int numCellsHeight;
	
	/*
	 * Where to start drawing X Pixel
	 * Used for centering GameBoard
	 */
	public int startYPixel;
	
	/*
	 * Where to start drawing Y Pixel
	 * Used for centering GameBoard
	 */
	public int startXPixel;
	
	/*
	 * Represents the maximum amount of towers that could
	 * theoretically be built
	 */
	public int maxTowers;
	
	public int actualWidth;
	
	public int actualHeight;
	
	public int actualMenuWidth;
	
	public Context context;
	
	public int towerWidthPx;
	public int actualBottomMenuHeight;
	
	private Paint androidPaint;
	private Paint reservePaint;
	
	public Node startNode, targetNode;
	public Cell startCell;
	
	public float densityScalar;
	
	
	/*
	 * Constructor computes cell layout, metrics, etc.
	 */
	public GameBoard(int width, int height, Context context) {
		densityScalar = context.getResources().getDisplayMetrics().density;
		if(MainActivity.DEBUG) {
			Log.d(TAG, "width: " + width + ", height: " + height);
			Log.d(TAG, "densityscalar:" + densityScalar);
		}
		this.context = context;
		actualWidth = width;
		actualHeight = height;
		int actualCellsAcross = (int) (INIT_NUM_CELLS_ACROSS * densityScalar);
		actualMenuWidth = (int) (MINIMUM_MENU_WIDTH * densityScalar);
		actualBottomMenuHeight = (int) (MINIMUM_BOTTOM_MENU_HEIGHT * densityScalar);
		width = width - actualMenuWidth; //account for menu
		height = height - actualBottomMenuHeight;
		cellSideLength = width / actualCellsAcross;
		int missing = width % actualCellsAcross;
		int numCellsToAddAcross = missing / cellSideLength;
		startXPixel = (width % cellSideLength) / 2;
		startYPixel = (height % cellSideLength) / 2;
		numCellsWide = actualCellsAcross + numCellsToAddAcross;
		numCellsHeight = height / cellSideLength;
		numCells = numCellsWide * numCellsHeight;
		cellGrid = new Cell[numCellsWide][numCellsHeight];
		towerWidthPx = AbstractTower.TOWER_SIDE_LENGTH * cellSideLength;
		
		androidPaint = new Paint();
		reservePaint = new Paint();
		androidPaint.setColor(0xff74AC23);
		reservePaint.setColor(0xff74AC23);
		reservePaint.setAlpha(175);
		
		initCellGrid();
		reserveCells();
		calculateMaximumNumberOfTowers();
		
		nodes = new Node[getNodesWidth()][getNodesHigh()];
		for (int x=0;x<getNodesWidth();x++) {
			for (int y=0;y<getNodesHigh();y++) {
				nodes[x][y] = new Node(x,y,this);
			}
		}
		
		if (MainActivity.DEBUG) {
			Log.d(TAG, "Max Towers=" + maxTowers);
			Log.d(TAG, "Actual Menu Width=" + actualMenuWidth);
			Log.d(TAG, "Cell Side Length=" + cellSideLength);
			Log.d(TAG, "Start X Pixel=" + startXPixel + " Start Y Pixel=" + startYPixel);
			Log.d(TAG, "NumCellsWide=" + getNumCellsWide() +  " NumCellsHigh=" + getNumCellsHeight());
		}
	}
	
	/*TODO: FIX THIS!! */
	private void calculateMaximumNumberOfTowers() {
		maxTowers = ((numCellsHeight - MINIMUM_MONSTER_PATH) / AbstractTower.TOWER_SIDE_LENGTH) * 
		((numCellsWide - (2 * MINIMUM_MONSTER_PATH)) / (AbstractTower.TOWER_SIDE_LENGTH + MINIMUM_MONSTER_PATH));
	}
	
	private void initCellGrid() {
		for(int x = 0; x < numCellsWide; x++) {
			for(int y = 0; y < numCellsHeight; y++) {
				cellGrid[x][y] = new Cell(y, x, cellSideLength, this, isTopEdge(x,y), isBottomEdge(x,y));
			}
		}
	}
	
	public boolean isTopEdge(int x, int y) {
		if(y <= (numCellsHeight - 1) / 2) {
			if(x > numCellsWide - 1 - MINIMUM_MONSTER_PATH || x < MINIMUM_MONSTER_PATH) return true;
			else if (y < MINIMUM_MONSTER_PATH) return true;
		}
		return false;
	}
	
	public boolean isBottomEdge(int x, int  y) {
		if(y <= (numCellsHeight - 1) / 2) return false;
		else {
			if(x > numCellsWide - 1 - MINIMUM_MONSTER_PATH || x < MINIMUM_MONSTER_PATH) return true;
			else if (y > numCellsHeight - 1 - MINIMUM_MONSTER_PATH) return true;
		}
		return false;
	}
	
	public boolean hasTopEdgeTower(int x, int y) {
		for(int i = x; i < x + AbstractTower.TOWER_SIDE_LENGTH; i++){
			for(int j = y; j < y + AbstractTower.TOWER_SIDE_LENGTH; j++) {
				if (isTopEdge(i,j)) return true;
			}
		}
		return false;
	}
	
	public boolean hasBottomEdgeTower(int x, int y) {
		for(int i = x; i < x + AbstractTower.TOWER_SIDE_LENGTH; i++){
			for(int j = y; j < y + AbstractTower.TOWER_SIDE_LENGTH; j++) {
				if (isBottomEdge(i,j)) return true;
			}
		}
		return false;
	}
	
	private void reserveCells() {//TODO rewrite this
		int midPoint = numCellsHeight / 2 - 1;
		//Log.d(TAG, ""+ midPoint);
		startNode = new Node(0, midPoint, this);
		startCell = startNode.occupiedCells[0];
		targetNode = new Node(numCellsWide - 3, midPoint, this);
		int y;
		for(y = 0; y < MINIMUM_MONSTER_PATH; y++) {
			Log.d(TAG, "reserving cells");
			cellGrid[0][midPoint + y].content = Cell.RESERVED;
			cellGrid[0][midPoint - y].content = Cell.RESERVED;
			cellGrid[1][midPoint + y].content = Cell.RESERVED;
			cellGrid[1][midPoint - y].content = Cell.RESERVED;
			cellGrid[2][midPoint + y].content = Cell.RESERVED;
			cellGrid[2][midPoint - y].content = Cell.RESERVED;
			cellGrid[numCellsWide -1][midPoint + y].content = Cell.RESERVED;
			cellGrid[numCellsWide -1][midPoint - y].content = Cell.RESERVED;
			cellGrid[numCellsWide -2][midPoint + y].content = Cell.RESERVED;
			cellGrid[numCellsWide -2][midPoint - y].content = Cell.RESERVED;
			cellGrid[numCellsWide -3][midPoint + y].content = Cell.RESERVED;
			cellGrid[numCellsWide -3][midPoint - y].content = Cell.RESERVED;
		}
		cellGrid[0][midPoint + y].content = Cell.RESERVED;
		cellGrid[1][midPoint + y].content = Cell.RESERVED;
		cellGrid[2][midPoint + y].content = Cell.RESERVED;
		cellGrid[numCellsWide -1][midPoint + y].content = Cell.RESERVED;
		cellGrid[numCellsWide -2][midPoint + y].content = Cell.RESERVED;
		cellGrid[numCellsWide -3][midPoint + y].content = Cell.RESERVED;
		cellGrid[0][midPoint + y +1].content = Cell.RESERVED;
		cellGrid[1][midPoint + y + 1].content = Cell.RESERVED;
		cellGrid[2][midPoint + y + 1].content = Cell.RESERVED;
		cellGrid[numCellsWide -1][midPoint + y + 1].content = Cell.RESERVED;
		cellGrid[numCellsWide -2][midPoint + y + 1].content = Cell.RESERVED;
		cellGrid[numCellsWide -3][midPoint + y + 1].content = Cell.RESERVED;
	}
	
	public Cell[][] getCellGrid() {
		return cellGrid;
	}
	
	public void drawGameBoard(Canvas canvas) {
		drawGridLines(canvas);
		drawReservedCells(canvas);
		//drawTowerLabeledCells(canvas);
		//drawCurrentPath(canvas);
	}
	
	/*Used for testing purposes*/
	private void drawTowerLabeledCells(Canvas canvas) {
		for(int x = 0; x< numCellsWide; x++) {
			for(int y = 0; y < numCellsHeight; y++) {
				if(cellGrid[x][y].content == Cell.TOWER)
					canvas.drawRect(cellGrid[x][y].pxColumn, cellGrid[x][y].pxRow, cellGrid[x][y].pxColumn + cellSideLength, cellGrid[x][y].pxRow + cellSideLength, reservePaint);
			}
		}
	}
	
	private void drawReservedCells(Canvas canvas) {
		for(int y = 0; y < numCellsHeight; y++) {
			Cell tempCell = cellGrid[0][y];
			Cell tempCell1 = cellGrid[1][y];
			Cell tempCell2 = cellGrid[numCellsWide - 1][y];
			Cell tempCell3 = cellGrid[numCellsWide - 2][y];
			Cell tempCell4 = cellGrid[2][y];
			Cell tempCell5 = cellGrid[numCellsWide - 3][y];
			if(tempCell.content == Cell.RESERVED) {
				canvas.drawRect(tempCell.pxColumn, tempCell.pxRow, tempCell.pxColumn + cellSideLength, tempCell.pxRow + cellSideLength, reservePaint);
				canvas.drawRect(tempCell1.pxColumn, tempCell1.pxRow, tempCell1.pxColumn + cellSideLength, tempCell1.pxRow + cellSideLength, reservePaint);
				canvas.drawRect(tempCell2.pxColumn, tempCell2.pxRow, tempCell2.pxColumn + cellSideLength, tempCell2.pxRow + cellSideLength, reservePaint);
				canvas.drawRect(tempCell3.pxColumn, tempCell3.pxRow, tempCell3.pxColumn + cellSideLength, tempCell3.pxRow + cellSideLength, reservePaint);
				canvas.drawRect(tempCell4.pxColumn, tempCell4.pxRow, tempCell4.pxColumn + cellSideLength, tempCell4.pxRow + cellSideLength, reservePaint);
				canvas.drawRect(tempCell5.pxColumn, tempCell5.pxRow, tempCell5.pxColumn + cellSideLength, tempCell5.pxRow + cellSideLength, reservePaint);
			}
		}
	}
	
	private void drawGridLines(Canvas canvas) {
		for(int x = 1; x < numCellsWide; x++) { //vertical lines
			canvas.drawLine(cellGrid[x][0].pxColumn, startYPixel,
					cellGrid[x][0].pxColumn, numCellsHeight * cellSideLength + startYPixel, androidPaint);
		}
		for(int y = 1; y < numCellsHeight; y++) { //horiz lines
			canvas.drawLine(startXPixel, cellGrid[0][y].pxRow,
					numCellsWide * cellSideLength + startXPixel, cellGrid[0][y].pxRow, androidPaint);
		}	
	}
	
	public int getNumCells() {
		return numCells;
	}
	
	public int getNumCellsWide() {
		return numCellsWide;
	}
	
	public int getNumCellsHeight() {
		return numCellsHeight;
	}
	
	public int getMaximumNumberOfTowers() {
		return maxTowers;
	}
	
	public int getStartXPixel() {
		return startXPixel;
	}
	
	public int getStartYPixel() {
		return startXPixel;
	}
	
	/*
	 * accepts x, y pixel and returns nearest Cell
	 */
	public Cell getCellNumIndex(int x, int y) {
		x = x + startXPixel;
		y = y + startYPixel;
		int cellIndexHeight = x / cellSideLength;
		int cellIndexWidth = y / cellSideLength;
		//Log.d(TAG,"cellIndexHeight: " + cellIndexHeight + " cellIndexWidth: " + cellIndexWidth);
		
		if (cellIndexWidth > numCellsWide) cellIndexWidth = numCellsWide - 1;
		else if (cellIndexWidth < 0) cellIndexWidth = 0;
		if (cellIndexHeight > numCellsHeight) cellIndexHeight = numCellsHeight - 1;
		else if (cellIndexHeight < 0) cellIndexHeight = 0;
		
		//Log.d(TAG,"cellIndexHeight: " + cellIndexHeight + " cellIndexWidth: " + cellIndexWidth);
		
		return cellGrid[cellIndexWidth][cellIndexHeight];
		
	}
		
	public boolean addTowerGameBoard(Cell topLeftCell, AbstractTower newTower) {
		try {
			for(int x = 0; x < AbstractTower.TOWER_SIDE_LENGTH; x++)
				for(int y = 0; y < AbstractTower.TOWER_SIDE_LENGTH; y++) {
					cellGrid[topLeftCell.column + x][topLeftCell.row + y].occupiedTower = newTower;
					cellGrid[topLeftCell.column + x][topLeftCell.row + y].content = Cell.TOWER;
					newTower.cellsOccupied[x][y] = cellGrid[topLeftCell.column + x][topLeftCell.row + y];
				}
		} catch(ArrayIndexOutOfBoundsException e) {return false;}
		return true;
	}
	
	public boolean removeTowerGameBoard(Cell topLeftCell) {
		try { 
			for(int x = 0; x < AbstractTower.TOWER_SIDE_LENGTH; x++)
				for(int y = 0; y < AbstractTower.TOWER_SIDE_LENGTH; y++) {
					cellGrid[topLeftCell.column + x][topLeftCell.row + y].occupiedTower = null;
					cellGrid[topLeftCell.column + x][topLeftCell.row + y].content = Cell.BLANK;
				}
		} catch(ArrayIndexOutOfBoundsException e) {return false;}
		return true;
	}
	
	public void addTempTower(Cell topLeftCell){
		for(int x = 0; x < AbstractTower.TOWER_SIDE_LENGTH; x++)
			for(int y = 0; y < AbstractTower.TOWER_SIDE_LENGTH; y++) {
				cellGrid[topLeftCell.column + x][topLeftCell.row + y].content = Cell.TOWER;
			}
	}
	public void removeTempTower(Cell topLeftCell){
		for(int x = 0; x < AbstractTower.TOWER_SIDE_LENGTH; x++)
			for(int y = 0; y < AbstractTower.TOWER_SIDE_LENGTH; y++) {
				cellGrid[topLeftCell.column + x][topLeftCell.row + y].content = Cell.BLANK;
			}
	}
	
	/**Used to get node width/height for pathfinding, node is the same size as enemy*/
	public int getNodesWidth(){
		//-2 for Enemy size 3
		return numCellsWide-2;
	}
	public int getNodesHigh(){
		//-2 for Enemy size 3
		return numCellsHeight-2;
	}
	
	public void restCellPathBooleans() {
		for(int x = 0; x < numCellsWide; x++) {
			for(int y = 0; y < numCellsHeight; y++) {
				cellGrid[x][y].isInPath = false;
			}
		}
	}
	
	public boolean isOnCurrentPath(Cell topLeftCell){
		for(int x = 0; x < AbstractTower.TOWER_SIDE_LENGTH; x++)
			for(int y = 0; y < AbstractTower.TOWER_SIDE_LENGTH; y++) {
				if(cellGrid[topLeftCell.column + x][topLeftCell.row + y].isInPath == true) return true;
			}
		return false;
	}
	
	public Cell getNearestCell(int x, int y) {
		x -= startXPixel;
		y -= startYPixel;
		try{
			return cellGrid[x / cellSideLength][y / cellSideLength];
		}catch(ArrayIndexOutOfBoundsException e){return null;}
	}
}
